/**
 * Copyright 2011 Mathieu Magnaudet
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package edu.enac.geoapp.wims;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

import edu.enac.geoapp.wims.exception.WimsException;
import edu.enac.geoapp.wims.model.CATProduct;
import edu.enac.geoapp.wims.model.CBProduct;
import edu.enac.geoapp.wims.model.ForecastSet;
import edu.enac.geoapp.wims.model.ICEProduct;
import edu.enac.geoapp.wims.model.Point;
import edu.enac.geoapp.wims.model.Product;
import edu.enac.geoapp.wims.model.ProductBuilderUtilities;
import edu.enac.geoapp.wims.model.WeatherProduct;

/**
 * Content handler for the parsing of the product specific parts of the GML files
 * @author mathieu
 *
 */
public class ProductContentHandler implements ContentHandler {

	private boolean inForecastSet = false;
	private boolean inStatusWeatherProduct = false;
	private boolean inCoverageArea = false;
	private boolean inExterior = false;
	private String currentName;
	private WeatherProduct weatherProduct;
	private Product product;
	private ForecastSet forecastSet;
	private StringBuffer buffer;
	private boolean inPointList = false;
	private Logger logger = Logger.getLogger(ProductContentHandler.class);
	private boolean inWeatherProductList = false;

	/**
	 * Constructor specifying the product type
	 * @param product
	 */
	public ProductContentHandler(WeatherProduct product) {
		this.weatherProduct = product;
	}

	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		String content = new String(ch, start, length);
		if (!inPointList)
			content = content.trim();
		if (buffer != null)
			buffer.append(content);
	}

	@Override
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub

	}

	@Override
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		if (localName.contains("forecastSet")) {
			inForecastSet = false;
			weatherProduct.addForecastSet(forecastSet);
		}

		if (localName.contains("StatusWeatherProduct")) {
			inStatusWeatherProduct = false;
		}

		if (localName.contains("coverageArea"))
			inCoverageArea = false;

		if (localName.contains("weatherProductList"))
			inWeatherProductList = false;

		if (localName.contains("geometry")
				|| localName.contains("gravityCentre"))
			inPointList = false;

		if (localName.contains("exterior"))
			inExterior = false;

		// if I am in the list of weather product then delegate the
		// data processing to the right method
		if (inWeatherProductList) {
			if (localName.contains("confidenceLevel")) {
				product.setConfidenceLevel(Integer.parseInt(buffer.toString()));
				buffer = null;
			}

			else if (localName.contains("swpId")) {
				product.setSwpId(buffer.toString());
				buffer = null;
			}

			else if (localName.contains("posList")) {
				try {
					if (inExterior) {
						List<Point> exterior = new ArrayList<Point>();
						ProductBuilderUtilities.addPoint(exterior, buffer.toString());
						product.getPolygon().setExteriorPoints(exterior);
					} else {
						List<Point> hole =new ArrayList<Point>();
						ProductBuilderUtilities.addPoint(hole, buffer.toString());
						product.getPolygon().addHole(hole);
					}
				} catch (WimsException e) {
					logger.warn(e.getMessage());
					forecastSet.getCoverage().setExteriorPoints(null);
				}
				buffer = null;
			} else {
				switch (weatherProduct.getProductType()) {
				case CAT:
					endCATElement(uri, localName, qName);
					break;
				case CB:
					endCBElement(uri, localName, qName);
					break;
				case ICE:
					endICEElement(uri, localName, qName);
					break;
				default:
					break;
				}
			}
		}

		if (inForecastSet && inStatusWeatherProduct) {
			if (localName.contains("areaName")) {
				forecastSet.getMetada().setAreaName(buffer.toString());
				buffer = null;
			}
			if (localName.contains("issuedTime")) {
				forecastSet.getMetada().getTimeParameters()
						.setIssuedTime(buffer.toString());
				buffer = null;
			}
			if (localName.contains("refreshTime")) {
				forecastSet.getMetada().getTimeParameters()
						.setRefreshTime(buffer.toString());
				buffer = null;
			}
			if (localName.contains("analysisTime")) {
				forecastSet.getMetada().getTimeParameters()
						.setAnalysisTime(buffer.toString());
				buffer = null;
			}
			if (localName.contains("validityStartTime")) {
				forecastSet.getMetada().getTimeParameters()
						.setValidityStartTime(buffer.toString());
				buffer = null;
			}
			if (localName.contains("validityEndTime")) {
				forecastSet.getMetada().getTimeParameters()
						.setValidityEndTime(buffer.toString());
				buffer = null;
			}

		}

		if (inCoverageArea) {
			if (localName.contains("top")) {
				forecastSet.getCoverage().setTop(
						Float.parseFloat(buffer.toString()));
				buffer = null;
			}
			if (localName.contains("bottom")) {
				forecastSet.getCoverage().setBottom(
						Float.parseFloat(buffer.toString()));
				buffer = null;
			}

			if (localName.contains("posList")) {

				try {
					if (inExterior) {
						List<Point> exterior = new ArrayList<Point>();
						ProductBuilderUtilities.addPoint(exterior, buffer
								.toString());
						forecastSet.getCoverage().setExteriorPoints(exterior);
					} else {
						List<Point> hole = new ArrayList<Point>();
						ProductBuilderUtilities.addPoint(hole, buffer
								.toString());
						forecastSet.getCoverage().addHole(hole);
					}
				} catch (WimsException e) {
					logger.warn(e.getMessage());
					forecastSet.getCoverage().setExteriorPoints(null);
				}
				buffer = null;
			}
		}
	}

	private void endICEElement(String uri, String localName, String qName) {
		if (localName.toLowerCase().contains("ice")) {
			product.getPolygon().buildPolygon();
			forecastSet.addProduct(product);
			product = null;
		}

		if (product != null) {
			for (String field : ((ICEProduct) product).getFieldList()) {
				if (localName.contains(field)) {
					product.initField(field, buffer.toString());
					buffer = null;
				}
			}
		}
	}

	private void endCBElement(String uri, String localName, String qName) {
		if (localName.contains("CB")) {
			product.getPolygon().buildPolygon();
			forecastSet.addProduct(product);
			product = null;
		}
		if (product != null) {
			for (String field : ((CBProduct) product).getFieldList()) {
				if (localName.contains(field)) {
					product.initField(field, buffer.toString());
					buffer = null;
				}
			}
		}
	}

	private void endCATElement(String uri, String localName, String qName) {
		if (localName.contains("CAT")) {
			product.getPolygon().buildPolygon();
			forecastSet.addProduct(product);
			product = null;
		}
		if (product != null) {
			for (String field : ((CATProduct) product).getFieldList()) {
				if (localName.contains(field)) {
					product.initField(field, buffer.toString());
					buffer = null;
				}
			}
		}

	}

	@Override
	public void endPrefixMapping(String arg0) throws SAXException {
		// TODO Auto-generated method stub

	}

	@Override
	public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
			throws SAXException {
		// TODO Auto-generated method stub

	}

	@Override
	public void processingInstruction(String arg0, String arg1)
			throws SAXException {
		// TODO Auto-generated method stub

	}

	@Override
	public void setDocumentLocator(Locator arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void skippedEntity(String arg0) throws SAXException {
		// TODO Auto-generated method stub

	}

	@Override
	public void startDocument() throws SAXException {
		// TODO Auto-generated method stub

	}

	@Override
	public void startElement(String uri, String localName, String qName,
			Attributes atts) throws SAXException {
		this.currentName = localName;
		// test where I am in the file
		if (localName.contains("forecastSet")) {
			inForecastSet = true;
			forecastSet = new ForecastSet();
		}

		if (localName.contains("StatusWeatherProduct"))
			inStatusWeatherProduct = true;

		if (localName.contains("coverageArea"))
			inCoverageArea = true;

		if (localName.contains("exterior"))
			inExterior = true;

		if (localName.contains("weatherProductList"))
			inWeatherProductList = true;

		if (localName.contains("geometry")
				|| localName.contains("gravityCenter"))
			inPointList = true;

		// if I am in the list of weather product then delegate the
		// data processing to the right method
		if (inWeatherProductList) {
			initBuffFor("swpId");
			initBuffFor("confidenceLevel");
			switch (weatherProduct.getProductType()) {
			case CAT:
				startCATElement(uri, localName, qName, atts);
				break;
			case CB:
				startCBElement(uri, localName, qName, atts);
				break;
			case ICE:
				startICEElement(uri, localName, qName, atts);
				break;
			default:
				break;
			}
		}

		// then init buff if required
		if (inForecastSet && inStatusWeatherProduct) {
			initBuffFor("areaName");
			initBuffFor("issuedTime");
			initBuffFor("refreshTime");
			initBuffFor("analysisTime");
			initBuffFor("validityStartTime");
			initBuffFor("validityEndTime");
			initBuffFor("confidenceLevel");
			initBuffFor("swpId");
		}

		if (inCoverageArea) {
			initBuffFor("top");
			initBuffFor("bottom");
			initBuffFor("posList");
		}

		if (inPointList) {
			initBuffFor("posList");
		}
	}

	private void startICEElement(String uri, String localName, String qName,
			Attributes atts) {
		if (localName.toLowerCase().contains("ice")) {
			product = new ICEProduct();
		}
		if (product != null) {
			for (String field : ((ICEProduct) product).getFieldList()) {
				initBuffFor(field);
			}
		}
	}

	private void startCBElement(String uri, String localName, String qName,
			Attributes atts) {
		if (localName.toLowerCase().contains("cb")) {
			product = new CBProduct();
		}
		if (product != null) {
			for (String field : ((CBProduct) product).getFieldList()) {
				initBuffFor(field);
			}
		}
	}

	private void startCATElement(String uri, String localName, String qName,
			Attributes atts) {
		if (localName.contains("CAT")) {
			product = new CATProduct();
		}
		if (product != null) {
			for (String field : ((CATProduct) product).getFieldList()) {
				initBuffFor(field);
			}
		}
	}

	@Override
	public void startPrefixMapping(String arg0, String arg1)
			throws SAXException {
		// TODO Auto-generated method stub

	}

	private void initBuffFor(String strToTest) {
		if (currentName.contains(strToTest))
			buffer = new StringBuffer();
	}
}
